#include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include "esp_check.h"
#include "esp_partition.h"
#include "driver/gpio.h"
#include "tinyusb.h"
#include "tusb_msc_storage.h"
#include "esp_log.h"
#include "SDIO_SD_card.h"
#include "diskio_impl.h"
#include "tinyusb_default_config.h"
#include "diskio_sdmmc.h"

#define TAG "tusb_msc"

// 挂载分区的基础路径
#define BASE_PATH "/sdcard"
/**
 * @brief 存储挂载/卸载状态改变回调函数
 *
 * 当应用程序挂载或卸载存储时，会调用此回调函数。
 * 这对于监控存储状态变化非常有用。
 *
 * @param handle 存储设备句柄
 * @param event MSC事件指针，包含挂载改变数据
 * @param arg 用户参数
 */
static void storage_mount_changed_cb(tinyusb_msc_storage_handle_t handle, tinyusb_msc_event_t *event, void *arg)
{
    switch (event->id) {
        case TINYUSB_MSC_EVENT_MOUNT_START:
            ESP_LOGI(TAG, "存储挂载/卸载操作开始");
            break;
        case TINYUSB_MSC_EVENT_MOUNT_COMPLETE:
            ESP_LOGI(TAG, "存储挂载/卸载操作完成，挂载点: %s", 
                     event->mount_point == TINYUSB_MSC_STORAGE_MOUNT_USB ? "USB" : "应用程序");
            break;
        case TINYUSB_MSC_EVENT_MOUNT_FAILED:
            ESP_LOGE(TAG, "存储挂载/卸载操作失败");
            break;
        case TINYUSB_MSC_EVENT_FORMAT_REQUIRED:
            ESP_LOGW(TAG, "存储需要格式化");
            break;
        case TINYUSB_MSC_EVENT_FORMAT_FAILED:
            ESP_LOGE(TAG, "存储格式化失败");
            break;
        default:
            ESP_LOGW(TAG, "未知存储事件: %d", event->id);
            break;
    }
}

/**
 * @brief 初始化SDMMC存储
 *
 * 该函数负责初始化SD卡作为USB MSC存储介质。
 * 它会配置SDMMC主机和插槽，初始化SD卡，并处理SD卡检测和初始化过程。
 * 这是USB MSC存储设备使用SD卡作为存储介质的关键初始化函数。
 *
 * @param card SDMMC卡指针的指针，用于返回初始化的SD卡对象
 * @return esp_err_t 执行结果：ESP_OK表示成功，其他表示失败
 */
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
{
    esp_err_t ret = ESP_OK;
    bool host_init = false;
    sdmmc_card_t *sd_card;

    ESP_LOGI(TAG, "初始化SD卡");

    // 默认情况下，SD卡频率初始化为SDMMC_FREQ_DEFAULT（20MHz）
    // 要设置特定频率，请使用host.max_freq_khz（SDMMC范围为400kHz - 40MHz）
    // 示例：对于固定频率10MHz，使用host.max_freq_khz = 10000;
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();

    // 此初始化不使用卡检测（CD）和写保护（WP）信号的插槽。
    // 如果您的板子有这些信号，请修改slot_config.gpio_cd和slot_config.gpio_wp。
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();

    // 对于SD卡，设置要使用的总线宽度
    slot_config.width = 1; // 1位总线宽度
    // 在可以使用GPIO矩阵配置SD卡使用的GPIO的芯片上，设置用户定义的值
    slot_config.clk = BSP_SD_CLK; // 时钟引脚
    slot_config.cmd = BSP_SD_CMD; // 命令引脚
    slot_config.d0 = BSP_SD_D0;   // 数据线0
    // 在启用的引脚上启用内部上拉。内部上拉不足，
    // 但请确保总线上连接了10k外部上拉。
    // 这仅用于调试/示例目的。
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

    // 不使用ff_memalloc，因为优先在内部RAM中分配
    sd_card = (sdmmc_card_t *)malloc(sizeof(sdmmc_card_t));
    ESP_GOTO_ON_FALSE(sd_card, ESP_ERR_NO_MEM, clean, TAG, "无法分配新的sdmmc_card_t");

    ESP_GOTO_ON_ERROR((*host.init)(), clean, TAG, "主机配置初始化失败");
    host_init = true;

    ESP_GOTO_ON_ERROR(sdmmc_host_init_slot(host.slot, (const sdmmc_slot_config_t *)&slot_config),
                      clean, TAG, "主机初始化插槽失败");

    // 循环尝试初始化SD卡，直到成功或用户插入SD卡
    int retry_count = 0;
    const int max_retries = 10;
    while (sdmmc_card_init(&host, sd_card) && retry_count < max_retries)
    {
        ESP_LOGE(TAG, "插槽的检测引脚断开（插入uSD卡）。正在重试... (%d/%d)", ++retry_count, max_retries);
        vTaskDelay(pdMS_TO_TICKS(3000)); // 等待3秒后重试
    }

    if (retry_count >= max_retries) {
        ESP_LOGE(TAG, "SD卡初始化失败，达到最大重试次数");
        ret = ESP_FAIL;
        goto clean;
    }

    // 卡已初始化，打印其属性
    sdmmc_card_print_info(stdout, sd_card);
    *card = sd_card;

    return ESP_OK;

clean:
    // 清理资源：如果主机已初始化，则反初始化；如果分配了内存，则释放
    if (host_init)
    {
        if (host.flags & SDMMC_HOST_FLAG_DEINIT_ARG)
        {
            host.deinit_p(host.slot);
        }
        else
        {
            (*host.deinit)();
        }
    }
    if (sd_card)
    {
        free(sd_card);
        sd_card = NULL;
    }
    return ret;
}

/**
 * @brief 主应用程序入口函数
 *
 * 该函数负责初始化存储设备、配置USB MSC设备、设置控制台等操作。
 * 这是整个USB MSC示例应用程序的启动点，负责协调所有组件的初始化。
 */
void tusb_msc_init(void)
{
    ESP_LOGI(TAG, "初始化USB MSC存储设备...");

    // 初始化SD卡
    static sdmmc_card_t *card = NULL;
    esp_err_t ret = storage_init_sdmmc(&card);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "SD卡初始化失败: %s", esp_err_to_name(ret));
        return;
    }

    // 配置USB MSC存储设备
    tinyusb_msc_storage_config_t cfg = {
        .medium = {
            .card = card
        },
        .fat_fs = {
            .base_path = BASE_PATH,
            .config = {
                .format_if_mount_failed = false,
                .max_files = 10,  // 增加最大文件数
                .allocation_unit_size = CONFIG_WL_SECTOR_SIZE
            },
            .do_not_format = false,
            .format_flags = 0  // 使用默认格式
        },
        .mount_point = TINYUSB_MSC_STORAGE_MOUNT_APP  // 应用程序初始拥有存储
    };

    // 配置MSC驱动
    tinyusb_msc_driver_config_t driver_cfg = {
        .user_flags = {
            .auto_mount_off = false  // 允许自动挂载
        },
        .callback = NULL,  // 使用单独的回调注册
        .callback_arg = NULL
    };

    // 安装MSC驱动
    ret = tinyusb_msc_install_driver(&driver_cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "MSC驱动安装失败: %s", esp_err_to_name(ret));
        return;
    }

    // 创建新的存储实例
    tinyusb_msc_storage_handle_t storage_hdl = NULL;
    ret = tinyusb_msc_new_storage_sdmmc(&cfg, &storage_hdl);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "创建MSC存储实例失败: %s", esp_err_to_name(ret));
        return;
    }

    // 注册挂载状态改变回调
    ESP_ERROR_CHECK(tinyusb_msc_set_storage_callback(storage_mount_changed_cb, NULL));

    // 安装TinyUSB驱动
    const tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
    ret = tinyusb_driver_install(&tusb_cfg);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "TinyUSB驱动安装失败: %s", esp_err_to_name(ret));
        return;
    }

    ESP_LOGI(TAG, "USB MSC初始化完成");
}

/**
 * @brief 获取存储设备句柄（供外部使用）
 *
 * @return tinyusb_msc_storage_handle_t 存储设备句柄
 */
tinyusb_msc_storage_handle_t tusb_msc_get_storage_handle(void)
{
    // 注意：需要在 tusb_msc_init() 之后调用
    static tinyusb_msc_storage_handle_t storage_hdl = NULL;
    return storage_hdl;
}

/**
 * @brief 检查存储设备是否可用
 *
 * @return true 可用
 * @return false 不可用
 */
bool tusb_msc_is_ready(void)
{
    return tinyusb_msc_storage_in_use_by_usb_host() == false;
}
